#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pd_linux.h"
#include "xconfig.h"
#include "xusb_common.h"
#include "xusbdevice.h"
#include "xlibusb.h"
#include "config_ch34x.h"
#include "config_cp210x.h"
#include "config_cdc.h"
#include "config_pl2303.h"
#include "config_ftdi.h"
#include "device_ids.h"
#include "xserialusb.h"

XUDC_API_BEGIN


#define SDK_VER_MAJOR   1
#define SDK_VER_MINOR   0
#define SDK_VER_BUILD   0

XUDC_DLLEXPORT void APICALL XSERIALUSB_API_PREFIX(version)(int *major, int *minor, int *build)
{
    if(NULL == major || NULL == minor || NULL == build)
    {
        return;
    }
    *major = SDK_VER_MAJOR;
    *minor = SDK_VER_MINOR;
    *build = SDK_VER_BUILD;
}

XUDC_DLLEXPORT int APICALL XSERIALUSB_API_PREFIX(enumdevice)(int vid, int pid, TXUSBDevice deviceList[], int maxCout)
{
    return x_libusb_enumdevice(vid, pid, deviceList, maxCout);
}

XUDC_DLLEXPORT int APICALL XSERIALUSB_API_PREFIX(searchdevice)(unsigned short vid, unsigned short pid, char *szSerialNumber, PXUSBDevice deviceInfo)
{
    return x_libusb_searchdevice(vid, pid, szSerialNumber, deviceInfo);
}

XUDC_DLLEXPORT void *APICALL XSERIALUSB_API_PREFIX(opendevice)(PXUSBDevice usbDevice, int baudRate)
{
    void *handle = x_libusb_opendevice(usbDevice);
    if(NULL != handle)
    {
        if(1 == IsCH34XDevice(usbDevice->vid, usbDevice->pid))
        {
            if(0 != config_ch34x::Init(handle))
            {
                x_libusb_closedevice(handle);
                return NULL;
            }
            config_ch34x::SetBaudRate(handle, baudRate);
            config_ch34x::SetDataBits(handle, 8);
            config_ch34x::SetStopBits(handle, 1);
            config_ch34x::SetParity(handle, 0);
            return handle;
        }
        else if(1 == IsCP210XDevice(usbDevice->vid, usbDevice->pid))
        {
            if(0 != config_cp210x::Init(handle))
            {
                x_libusb_closedevice(handle);
                return NULL;
            }
            config_cp210x::SetBaudRate(handle, baudRate);
            config_cp210x::SetDataBits(handle, 8);
            config_cp210x::SetStopBits(handle, 1);
            config_cp210x::SetParity(handle, 0);
            return handle;
        }
        else if(1 == IsCDCDevice(usbDevice->vid, usbDevice->pid))
        {
            if(0 != config_cdc::Init(handle))
            {
                x_libusb_closedevice(handle);
                return NULL;
            }
            config_cdc::SetBaudRate(handle, baudRate);
            config_cdc::SetDataBits(handle, 8);
            config_cdc::SetStopBits(handle, 1);
            config_cdc::SetParity(handle, 0);
            return handle;
        }
        else if(1 == IsPL2303Device(usbDevice->vid, usbDevice->pid))
        {
            if(0 != config_pl2303::Init(handle))
            {
                x_libusb_closedevice(handle);
                return NULL;
            }
            config_pl2303::SetBaudRate(handle, baudRate);
            config_pl2303::SetDataBits(handle, 8);
            config_pl2303::SetStopBits(handle, 1);
            config_pl2303::SetParity(handle, 0);
            return handle;
        }
        else if(1 == IsFTDIDevice(usbDevice->vid, usbDevice->pid))
        {
            if(0 != config_ftdi::Init(handle))
            {
                x_libusb_closedevice(handle);
                return NULL;
            }
            config_ftdi::SetBaudRate(handle, baudRate);
            config_ftdi::SetDataBits(handle, 8);
            config_ftdi::SetStopBits(handle, 1);
            config_ftdi::SetParity(handle, 0);
            return handle;
        }
        else
        {
            x_libusb_closedevice(handle);
            return NULL;
        }
    }
    return NULL;
}

XUDC_DLLEXPORT int APICALL XSERIALUSB_API_PREFIX(closedevice)(void *handle)
{
    int vid = 0;
    int pid = 0;
    int rev = 0;
    x_libusb_get_vid_pid_rev(handle, &vid, &pid, &rev);
    if(1 == IsCH34XDevice(vid, pid))
    {
        config_ch34x::Free(handle);
    }
    else if(1 == IsCP210XDevice(vid, pid))
    {
        config_cp210x::Free(handle);
    }
    else if(1 == IsCDCDevice(vid, pid))
    {
        config_cdc::Free(handle);
    }
    else if(1 == IsPL2303Device(vid, pid))
    {
        config_pl2303::Free(handle);
    }
    else if(1 == IsFTDIDevice(vid, pid))
    {
        config_ftdi::Free(handle);
    }
    return x_libusb_closedevice(handle);
}

#ifdef __ANDROID__
XUDC_DLLEXPORT void *APICALL XSERIALUSB_API_PREFIX(opendevice_fd)(int vid, int pid, int busNum, int dev_address, int fd, int baudRate)
{
    void *handle = x_libusb_opendevice_fd(vid, pid, busNum, dev_address, fd);
    if(NULL != handle)
    {
        if(1 == IsCH34XDevice(vid, pid))
        {
            if(0 != config_ch34x::Init(handle))
            {
                x_libusb_closedevice(handle);
                return NULL;
            }
            config_ch34x::SetBaudRate(handle, baudRate);
            config_ch34x::SetDataBits(handle, 8);
            config_ch34x::SetStopBits(handle, 1);
            config_ch34x::SetParity(handle, 0);
            return handle;
        }
        else if(1 == IsCP210XDevice(vid, pid))
        {
            if(0 != config_cp210x::Init(handle))
            {
                x_libusb_closedevice(handle);
                return NULL;
            }
            config_cp210x::SetBaudRate(handle, baudRate);
            config_cp210x::SetDataBits(handle, 8);
            config_cp210x::SetStopBits(handle, 1);
            config_cp210x::SetParity(handle, 0);
            return handle;
        }
        else if(1 == IsCDCDevice(vid, pid))
        {
            if(0 != config_cdc::Init(handle))
            {
                x_libusb_closedevice(handle);
                return NULL;
            }
            config_cdc::SetBaudRate(handle, baudRate);
            config_cdc::SetDataBits(handle, 8);
            config_cdc::SetStopBits(handle, 1);
            config_cdc::SetParity(handle, 0);
            return handle;
        }
        else if(1 == IsPL2303Device(vid, pid))
        {
            if(0 != config_pl2303::Init(handle))
            {
                x_libusb_closedevice(handle);
                return NULL;
            }
            config_pl2303::SetBaudRate(handle, baudRate);
            config_pl2303::SetDataBits(handle, 8);
            config_pl2303::SetStopBits(handle, 1);
            config_pl2303::SetParity(handle, 0);
            return handle;
        }
        else if(1 == IsFTDIDevice(vid, pid))
        {
            if(0 != config_ftdi::Init(handle))
            {
                x_libusb_closedevice(handle);
                return NULL;
            }
            config_ftdi::SetBaudRate(handle, baudRate);
            config_ftdi::SetDataBits(handle, 8);
            config_ftdi::SetStopBits(handle, 1);
            config_ftdi::SetParity(handle, 0);
            return handle;
        }
        else
        {
            x_libusb_closedevice(handle);
            return NULL;
        }
    }
    return NULL;
}

XUDC_DLLEXPORT void *APICALL XSERIALUSB_API_PREFIX(opendevice_usbhost)(JavaVM *jvm, JNIEnv *env, jobject obj, int baudRate)
{
    void *handle = x_libusb_opendevice_usbhost(jvm, env, obj);
    if(NULL != handle)
    {
        int vid = 0;
        int pid = 0;
        int rev = 0;
        x_libusb_get_vid_pid_rev(handle, &vid, &pid, &rev);
        if(1 == IsCH34XDevice(vid, pid))
        {
            if(0 != config_ch34x::Init(handle))
            {
                x_libusb_closedevice_usbhost(jvm, env, handle);
                return NULL;
            }
            config_ch34x::SetBaudRate(handle, baudRate);
            config_ch34x::SetDataBits(handle, 8);
            config_ch34x::SetStopBits(handle, 1);
            config_ch34x::SetParity(handle, 0);
            return handle;
        }
        else if(1 == IsCP210XDevice(vid, pid))
        {
            if(0 != config_cp210x::Init(handle))
            {
                x_libusb_closedevice_usbhost(jvm, env, handle);
                return NULL;
            }
            config_cp210x::SetBaudRate(handle, baudRate);
            config_cp210x::SetDataBits(handle, 8);
            config_cp210x::SetStopBits(handle, 1);
            config_cp210x::SetParity(handle, 0);
            return handle;
        }
        else if(1 == IsCDCDevice(vid, pid))
        {
            if(0 != config_cdc::Init(handle))
            {
                x_libusb_closedevice_usbhost(jvm, env, handle);
                return NULL;
            }
            config_cdc::SetBaudRate(handle, baudRate);
            config_cdc::SetDataBits(handle, 8);
            config_cdc::SetStopBits(handle, 1);
            config_cdc::SetParity(handle, 0);
            return handle;
        }
        else if(1 == IsPL2303Device(vid, pid))
        {
            if(0 != config_pl2303::Init(handle))
            {
                x_libusb_closedevice_usbhost(jvm, env, handle);
                return NULL;
            }
            config_pl2303::SetBaudRate(handle, baudRate);
            config_pl2303::SetDataBits(handle, 8);
            config_pl2303::SetStopBits(handle, 1);
            config_pl2303::SetParity(handle, 0);
            return handle;
        }
        else if(1 == IsFTDIDevice(vid, pid))
        {
            if(0 != config_ftdi::Init(handle))
            {
                x_libusb_closedevice_usbhost(jvm, env, handle);
                return NULL;
            }
            config_ftdi::SetBaudRate(handle, baudRate);
            config_ftdi::SetDataBits(handle, 8);
            config_ftdi::SetStopBits(handle, 1);
            config_ftdi::SetParity(handle, 0);
            return handle;
        }
        else
        {
            x_libusb_closedevice_usbhost(jvm, env, handle);
            return NULL;
        }
    }
    return NULL;
}

XUDC_DLLEXPORT int APICALL XSERIALUSB_API_PREFIX(closedevice_usbhost)(JavaVM *jvm, JNIEnv *env, void *handle)
{
    int vid = 0;
    int pid = 0;
    int rev = 0;
    x_libusb_get_vid_pid_rev(handle, &vid, &pid, &rev);
    if(1 == IsCH34XDevice(vid, pid))
    {
        config_ch34x::Free(handle);
    }
    else if(1 == IsCP210XDevice(vid, pid))
    {
        config_cp210x::Free(handle);
    }
    else if(1 == IsCDCDevice(vid, pid))
    {
        config_cdc::Free(handle);
    }
    else if(1 == IsPL2303Device(vid, pid))
    {
        config_pl2303::Free(handle);
    }
    else if(1 == IsFTDIDevice(vid, pid))
    {
        config_ftdi::Free(handle);
    }
    return x_libusb_closedevice_usbhost(jvm, env, handle);
}

#endif

XUDC_DLLEXPORT int APICALL XSERIALUSB_API_PREFIX(controltransfer)(void *handle, unsigned char bmRequestType, unsigned char bRequest, unsigned short wValue, unsigned short wIndex,
                                                                  unsigned char *data, unsigned short wLength, unsigned int timeout)
{
    return x_libusb_controltransfer(handle, bmRequestType, bRequest, wValue, wIndex,
                                    data, wLength, timeout);
}

XUDC_DLLEXPORT void APICALL XSERIALUSB_API_PREFIX(resetdevice)(void *handle)
{
    return x_libusb_resetdevice(handle);
}

XUDC_DLLEXPORT int APICALL XSERIALUSB_API_PREFIX(write)(void *handle, unsigned char *data, int length, int *transferred, unsigned int timeout)
{
    return x_libusb_transferwrite(handle, data, length, transferred, timeout);
}

XUDC_DLLEXPORT int APICALL XSERIALUSB_API_PREFIX(read)(void *handle, unsigned char *data, int length, int *transferred, unsigned int timeout)
{
    return x_libusb_transferread(handle, data, length, transferred, timeout);
}

XUDC_API_END